﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Drawing;
using System.Drawing.Imaging;
namespace LowLevelGraphics.SeamCarving
{
    /// <summary>
    /// Energy function, needed for computing the seams.
    /// </summary>
    public abstract class EnergyFunction
    {
        public byte[,] EnergyMap { get; protected set; }

        public abstract void Recompute(BitmapData bmd, Size size);
        public abstract void RecomputeSeam(BitmapData bmd, Size size, Seam seam);

        /// <summary>
        /// Gets the <see cref="System.Byte"/> with the specified x.
        /// </summary>
        /// <value></value>
        public byte this[int x, int y, Size size]
        {
            get
            {
                if (x >= 0 && x < size.Width && y >= 0 && y < size.Height)
                {
                    return EnergyMap[x, y];
                }
                else
                {
                    //pixel is out of bounds, so return maximum energy
                    return byte.MaxValue;
                }
            }
        }

        /// <summary>
        /// Gets the energy value.
        /// </summary>
        /// <param name="bmd">The BMD.</param>
        /// <param name="size">The size.</param>
        /// <param name="x">The x.</param>
        /// <param name="y">The y.</param>
        /// <returns></returns>
        public static byte GetEnergyValue(BitmapData bmd, Size size, int x, int y)
        {
            if (x >= 0 && x < size.Width && y >= 0 && y < size.Height)
            {
                unsafe
                {
                    byte* row = (byte*)bmd.Scan0 + (y * bmd.Stride) + (x * 3);
                    //RGB to luminance formula
                    return (byte)((0.216 * row[2]) + (0.7152 * row[1]) + (0.0722 * row[0]));
                }
            }
            else
            {
                //pixel is out of bounds, so return maximum energy
                return byte.MaxValue;
            }
        }

        /// <summary>
        /// Gets the energy pixel.
        /// </summary>
        /// <param name="bmd">The BMD.</param>
        /// <param name="size">The size.</param>
        /// <param name="x">The x.</param>
        /// <param name="y">The y.</param>
        /// <returns></returns>
        protected static byte GetEnergyPixel(BitmapData bmd, Size size, int x, int y)
        {
            x = Math.Max(x, 0);
            x = Math.Min(x, size.Width - 1);
            y = Math.Max(y, 0);
            y = Math.Min(y, size.Height - 1);
            return GetEnergyValue(bmd, size, x, y);
        }

        /// <summary>
        /// Normalizes the specified min.
        /// </summary>
        /// <param name="min">The min.</param>
        /// <param name="max">The max.</param>
        /// <param name="value">The value.</param>
        /// <returns></returns>
        public static byte Normalize(double min, double max, double value)
        {
            return (byte)((value - min) * byte.MaxValue / (max - min));
        }
    }
}
